#include "src/server/blog_cacher.h"

#include <functional>

#include "src/base/utils.h"
#include "src/net/event_loop.h"

namespace fio66 {
namespace detail
{
std::string_view GetFileType(const std::filesystem::path& file_name){
  const auto& ext = file_name.extension().string();
  if(ext == ".html") {
    return "text/html; charset=utf-8";
  } else if(ext == ".css") {
    return "text/css; charset=utf-8";
  } else if(ext == ".js") {
    return "application/javascript; charset=utf-8";
  } else if(ext == ".png") {
    return "image/png";
  } else if(ext == ".json") {
    return "application/json";
  } else if(ext == ".svg") {
    return "image/svg+xml";
  } else if (ext == ".woff2") {
    return "font/woff2";
  }
  return "text/html; charset=utf-8";
}
} // namespace detail


BlogCacher::BlogCacher(net::EventLoop* event_loop, std::string_view uri_path) : event_loop_(event_loop), uri_path_(uri_path) {}

BlogCacher::~BlogCacher() { event_loop_->Cancel(timer_id_); }

bool BlogCacher::Start() {
  UpdateCache();
  timer_id_ = event_loop_->RunEvery(kUpdateInterval, std::bind(&BlogCacher::UpdateCache, this));
  return true;
}

void BlogCacher::UpdateCache() {
  auto dir_files = utils::GetDirectoryFiles(uri_path_);
  for (const auto& i : dir_files) {
    if (i.file_size() == 0) {
      continue;
    }
    auto path = i.path();
    auto iter = file_cache_.find(i.path());
    auto last_write_time = i.last_write_time();
    if (iter == file_cache_.end()) {
      UpdateFile(i);
      file_cache_.insert(std::make_pair(i.path(), last_write_time));
      continue;
    }

    if (iter->second != last_write_time) {
      UpdateFile(i);
      iter->second = last_write_time;
    }
  }
}

void BlogCacher::UpdateFile(const std::filesystem::path& path) {
  auto file_content = utils::ReadFile(path.c_str());
  if (file_content) {
    auto relative_path = std::filesystem::relative(path, uri_path_);
    // FIXME：过多访问的情况下可能会阻塞该线程，或许应该用condition_varible条件等待
    std::unique_lock lk(mu_);
    auto iter = blog_file_.find(relative_path);
    if (iter != blog_file_.end()) {
      iter->second->file_content.swap(file_content.value());
    } else {
      blog_file_.insert(std::make_pair("/" / relative_path, std::make_shared<FileInfo>(detail::GetFileType(relative_path.filename()), std::move(file_content.value()))));
    }
  }
}

std::shared_ptr<BlogCacher::FileInfo> BlogCacher::GetFileInfo(const std::filesystem::path& path) {
  if(!path.empty())  {
    std::shared_lock lk(mu_);
    auto iter = blog_file_.find(path);
    if (iter != blog_file_.end()) {
      return iter->second;
    }
  }
  LOGE <<"Not find path:" << path << " in blog cacher";
  return nullptr;
}
}  // namespace fio66
